package cn.xutongxue.interview.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.xutongxue.interview.common.ResponseResult;
import cn.xutongxue.interview.constants.CommonConstant;
import cn.xutongxue.interview.entity.Question;
import cn.xutongxue.interview.entity.QuestionBankMap;
import cn.xutongxue.interview.entity.dto.question.QuestionQueryDto;
import cn.xutongxue.interview.entity.dto.question.QuestionRemoveBatch;
import cn.xutongxue.interview.entity.vo.QuestionVo;
import cn.xutongxue.interview.enums.Errors;
import cn.xutongxue.interview.es.entity.QuestionEsEntity;
import cn.xutongxue.interview.mapper.QuestionBankMapMapper;
import cn.xutongxue.interview.mapper.QuestionMapper;
import cn.xutongxue.interview.service.QuestionBankMapService;
import cn.xutongxue.interview.service.QuestionService;
import cn.xutongxue.interview.utils.SqlUtils;
import cn.xutongxue.interview.utils.ThrowUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @description 针对表【question(题目表)】的数据库操作Service实现
 * @createDate 2024-09-11 16:43:43
 */
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question>
        implements QuestionService {

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    private QuestionBankMapService questionBankMapService;

    @Override
    public ResponseResult<Page<QuestionVo>> listQuestionVoPage(QuestionQueryDto queryDto) {
        LambdaQueryWrapper<Question> wrapper = Wrappers.lambdaQuery(Question.class)
                .like(StringUtils.isNotBlank(queryDto.getTitle()), Question::getTitle, queryDto.getTitle());
        Page<Question> dtoPage = page(new Page<>(queryDto.getCurrent(), queryDto.getPageSize()), wrapper);
        // 封装vo分页对象类
        Page<QuestionVo> voPage = new Page<>(dtoPage.getCurrent(), dtoPage.getSize(), dtoPage.getTotal());
        if (CollUtil.isEmpty(dtoPage.getRecords())) {
            voPage.setRecords(new ArrayList<>());
        } else {
            List<QuestionVo> questionVos = dtoPage.getRecords().stream().map(QuestionVo::objToVo).collect(Collectors.toList());
            voPage.setRecords(questionVos);
        }
        return ResponseResult.ok(voPage);
    }

    @Override
    public List<Question> listQuestionWitheDelete(Date fiveMinuteAgo) {
        return this.baseMapper.listQuestionWithDelete(fiveMinuteAgo);
    }

    @Override
    public Page<Question> searchQuestionFromEs(QuestionQueryDto queryDto) {

        String title = queryDto.getTitle();
        // es起始页为0
        int current = queryDto.getCurrent() - 1;
        int pageSize = queryDto.getPageSize();
        String sortField = queryDto.getSortField();
        String sortOrder = queryDto.getSortOrder();

        // 构造查询条件
        BoolQueryBuilder builder = QueryBuilders.boolQuery();
        builder.filter(QueryBuilders.termQuery("isDelete", 0));

        if (StringUtils.isNotBlank(title)) {
            builder.should(QueryBuilders.matchQuery("title", title));
            builder.should(QueryBuilders.matchQuery("content", title));
            builder.should(QueryBuilders.matchQuery("answer", title));
            builder.minimumShouldMatch(1);
        }
        // 排序
        SortBuilder<?> sortBuilder = SortBuilders.scoreSort();
        if (StringUtils.isNotBlank(sortField)) {
            sortBuilder = SortBuilders.fieldSort(sortField);
            sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC);
        }
        // 封装分页
        PageRequest pageRequest = PageRequest.of(current, pageSize);
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(builder)
                .withPageable(pageRequest)
                .withSorts(sortBuilder)
                .build();
        SearchHits<QuestionEsEntity> searchHits = elasticsearchRestTemplate.search(searchQuery, QuestionEsEntity.class);
        // 封装分页对象
        Page<Question> questionPage = new Page<>();
        List<Question> questionList = new ArrayList<>();
        questionPage.setTotal(searchHits.getTotalHits());
        // 数据转换
        if (searchHits.hasSearchHits()) {
            List<SearchHit<QuestionEsEntity>> searchHitList = searchHits.getSearchHits();
            for (SearchHit<QuestionEsEntity> tmp : searchHitList) {
                questionList.add(QuestionEsEntity.esEntityToObj(tmp.getContent()));
            }
        }
        // 封装结果
        questionPage.setRecords(questionList);
        return questionPage;
    }

    @Override
    @Transactional
    public void deleteQuestionBatch(List<Long> questionIdList) {
        ThrowUtils.throwIf(CollUtil.isEmpty(questionIdList), Errors.PARAMS_ERROR, "待删除的题目列表为空");
        for (Long questionId : questionIdList) {
            // 删除题目
            boolean result = this.removeById(questionId);
            ThrowUtils.throwIf(!result, Errors.OPERATION_ERROR, "删除题目失败");
            // 删除题目题库关联
            LambdaQueryWrapper<QuestionBankMap> wrapper = Wrappers.lambdaQuery(QuestionBankMap.class)
                    .eq(QuestionBankMap::getQuestionId, questionId);
            result = questionBankMapService.remove(wrapper);
            ThrowUtils.throwIf(!result, Errors.OPERATION_ERROR, "删除题目题库关联失败");
        }

    }

    @Override
    public Page<Question> listQuestionByPage(QuestionQueryDto queryDto) {
        Integer current = queryDto.getCurrent();
        Integer pageSize = queryDto.getPageSize();
        QueryWrapper<Question> queryWrapper = this.getQueryWrapper(queryDto);
        return this.page(new Page<>(current, pageSize), queryWrapper);
    }

    @Override
    public QueryWrapper<Question> getQueryWrapper(QuestionQueryDto queryDto) {
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        if (queryDto == null) {
            return queryWrapper;
        }

        String title = queryDto.getTitle();
        String content = queryDto.getContent();
        String answer = queryDto.getAnswer();
        List<String> tags = queryDto.getTags();
        String searchText = queryDto.getSearchText();
        String sortField = queryDto.getSortField();
        String sortOrder = queryDto.getSortOrder();

        // 从多字段中搜索
        if (StringUtils.isNotBlank(searchText)) {
            // 需要拼接查询条件
            queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
        }
        // 模糊查询
        queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
        queryWrapper.like(StringUtils.isNotBlank(content), "content", content);
        queryWrapper.like(StringUtils.isNotBlank(answer), "answer", answer);

        // JSON 数组查询
        if (CollUtil.isNotEmpty(tags)) {
            for (String tag : tags) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        // SQL注入校验
        SqlInjectionUtils.check(sortField);
        // 排序规则
        queryWrapper.orderBy(true,
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                StrUtil.toUnderlineCase(sortField));
        return queryWrapper;
    }


}




